/*	WARNING: COPYRIGHT (C) 2017 XSENS TECHNOLOGIES OR SUBSIDIARIES WORLDWIDE. ALL RIGHTS RESERVED.
	THIS FILE AND THE SOURCE CODE IT CONTAINS (AND/OR THE BINARY CODE FILES FOUND IN THE SAME
	FOLDER THAT CONTAINS THIS FILE) AND ALL RELATED SOFTWARE (COLLECTIVELY, "CODE") ARE SUBJECT
	TO A RESTRICTED LICENSE AGREEMENT ("AGREEMENT") BETWEEN XSENS AS LICENSOR AND THE AUTHORIZED
	LICENSEE UNDER THE AGREEMENT. THE CODE MUST BE USED SOLELY WITH XSENS PRODUCTS INCORPORATED
	INTO LICENSEE PRODUCTS IN ACCORDANCE WITH THE AGREEMENT. ANY USE, MODIFICATION, COPYING OR
	DISTRIBUTION OF THE CODE IS STRICTLY PROHIBITED UNLESS EXPRESSLY AUTHORIZED BY THE AGREEMENT.
	IF YOU ARE NOT AN AUTHORIZED USER OF THE CODE IN ACCORDANCE WITH THE AGREEMENT, YOU MUST STOP
	USING OR VIEWING THE CODE NOW, REMOVE ANY COPIES OF THE CODE FROM YOUR COMPUTER AND NOTIFY
	XSENS IMMEDIATELY BY EMAIL TO INFO@XSENS.COM. ANY COPIES OR DERIVATIVES OF THE CODE (IN WHOLE
	OR IN PART) IN SOURCE CODE FORM THAT ARE PERMITTED BY THE AGREEMENT MUST RETAIN THE ABOVE
	COPYRIGHT NOTICE AND THIS PARAGRAPH IN ITS ENTIRETY, AS REQUIRED BY THE AGREEMENT.
*/

#include "xstimestamp.h"
#include "xstime.h"
#include "xsutctime.h"
#include <time.h>
#include <string.h>

/*! \class XsTimeStamp
	\brief This class contains method to set, retrieve and compare timestamps
*/

/*! \addtogroup cinterface C Interface
	@{
*/

/*! \relates XsTimeStamp \brief Set the stored time to \a miliseconds. */
void XsTimeStamp_setMilliSecondTime(XsTimeStamp* thisPtr, int64_t t)
{
	thisPtr->m_msTime = t;
}

/*! \relates XsTimeStamp \brief Get the time of day component of the stored timestamp in seconds as a double precision value. */
double XsTimeStamp_timeOfDay(const XsTimeStamp* thisPtr)
{
	return (double)(thisPtr->m_msTime % (24*60*60*1000))*0.001;
}

/*! \relates XsTimeStamp \brief Returns the number of seconds elapsed since the epoch as stored in the XsTimeStamp
*/
int64_t XsTimeStamp_secondTime(const XsTimeStamp* thisPtr)
{
	return thisPtr->m_msTime/1000;
}

/*! \relates XsTimeStamp \brief Returns the millisecond part of the time (in the range 0-999)
*/
int32_t XsTimeStamp_milliSecondPart(const XsTimeStamp* thisPtr)
{
	return (int32_t) (thisPtr->m_msTime % 1000);
}

/*! \relates XsTimeStamp \brief Returns the seconds part of the time (in the range 0-59)
*/
int32_t XsTimeStamp_secondPart(const XsTimeStamp* thisPtr)
{
	return (int32_t) ((thisPtr->m_msTime/(1000))%60);
}

/*! \relates XsTimeStamp \brief Returns the minutes part of the time (in the range 0-59)
*/
int32_t XsTimeStamp_minutePart(const XsTimeStamp* thisPtr)
{
	return (int32_t) ((thisPtr->m_msTime/(60*1000))%60);
}

/*! \relates XsTimeStamp \brief Returns the hours part of the time (in the range 0-23)
*/
int32_t XsTimeStamp_hourPart(const XsTimeStamp* thisPtr)
{
	return (int32_t) ((thisPtr->m_msTime/(60*60*1000))%24);
}

/*! \relates XsTimeStamp
	\brief Returns the current time in ms since the epoch (Jan 1st 1970)
	\see XsTime_timeStampNow
	\param dest The object to write the time to, may be 0 in which case only the return value is generated.
	\returns The current time in ms since the epoch (Jan 1st 1970)
*/
int64_t XsTimeStamp_now(XsTimeStamp* dest)
{
	return (int64_t) XsTime_timeStampNow(dest);
}

/*! \relates XsTimeStamp
	\brief Returns the maximum value of an %XsTimeStamp
*/
int64_t XsTimeStamp_maxValue(void)
{
	return 9223372036854775807LL;	//INT64_MAX;
}

/*! \relates XsTimeStamp
	\brief Creates an XsTimeStamp from an XsUtcTime object
	\param utc The UTC time to convert
	\returns The converted time in ms since the epoch (Jan 1st 1970)
*/
int64_t XsTimeStamp_fromUtcTime(struct XsTimeStamp* thisPtr, const struct XsUtcTime* utc)
{
	int64_t rv = -1;
	if (utc && utc->m_valid)
	{
		struct tm utctm;
		utctm.tm_year = utc->m_year;
		utctm.tm_mon = utc->m_month;
		utctm.tm_mday = utc->m_day;

		utctm.tm_hour = utc->m_hour;
		utctm.tm_min = utc->m_minute;
		utctm.tm_sec = utc->m_second;

		// unused
		utctm.tm_wday = 0;
		utctm.tm_yday = 0;
		utctm.tm_isdst = 0;

		rv = (((int64_t) mktime(&utctm)) * 1000LL) + (utc->m_nano / 1000000);
	}
	if (thisPtr)
		thisPtr->m_msTime = rv;
	return rv;
}

/*! \relates XsTimeStamp
	\brief Converts the timestamp into an XsUtcTime object
	\param utc The UTC time object to write the conversion result to
*/
void XsTimeStamp_toUtcTime(struct XsTimeStamp* thisPtr, struct XsUtcTime* utc)
{
	struct tm tmUtc;
#ifdef _WIN32
	__time64_t t;
	t = (__time64_t)(thisPtr->m_msTime / 1000);
	if (_gmtime64_s(&tmUtc, &t))
	{
		//in case of an error the result is an invalid XsUtctime
		utc->m_valid = 0;
		return;
	}
#elif (defined(__arm__) && defined(__ARMCC_VERSION))
	#warning Function is not thread-safe for this platform/toolchain
	time_t t;
	t = (time_t)(thisPtr->m_msTime / 1000);
	struct tm* tmUtcPtr = gmtime(&t);
	if (tmUtcPtr == 0)
	{
		utc->m_valid = 0;
		return;
	}
	memcpy(&tmUtc, tmUtcPtr, sizeof(tmUtc));
#else
	time_t t;
	t = (time_t)(thisPtr->m_msTime / 1000);
	if (gmtime_r(&t, &tmUtc) == 0)
	{
		//in case of an error the result is an invalid XsUtctime
		utc->m_valid = 0;
		return;
	}
#endif

	utc->m_day = (uint8_t) tmUtc.tm_mday;
	utc->m_hour = (uint8_t) tmUtc.tm_hour;
	utc->m_minute = (uint8_t) tmUtc.tm_min;
	utc->m_month = (uint8_t) tmUtc.tm_mon + 1;
	utc->m_nano = (uint32_t)((thisPtr->m_msTime % 1000) * 1e6);
	utc->m_second = (uint8_t) tmUtc.tm_sec;
	utc->m_year = (uint16_t) (tmUtc.tm_year + 1900);
	utc->m_valid = 1;
}

/*! @} */
